﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Practices.Prism.Commands;
using MyFramework.Common;

namespace MyFramework.Command.DelegateWatchCommand
{
    /// <summary>
    /// Command raise trigger
    /// </summary>
    public class CommandRaiseTrigger : RaiseTrigger
    {
        #region constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="CommandRaiseTrigger"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="propertiesToWatch">The properties to watch.</param>
        /// <param name="propertiesToRaise">The properties to raise.</param>
        public CommandRaiseTrigger(IEntity source, Expression<Func<IEntity, object>>[] propertiesToWatch, Expression<Func<object>>[] propertiesToRaise)
            : base(source, propertiesToWatch, propertiesToRaise)
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CommandRaiseTrigger"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="propertyToRaise">The property to raise.</param>
        /// <param name="propertiesToWatch">The properties to watch.</param>
        public CommandRaiseTrigger(IEntity source, Expression<Func<object>> propertyToRaise, params Expression<Func<IEntity, object>>[] propertiesToWatch)
            : this(source, propertiesToWatch, new Expression<Func<object>>[] { propertyToRaise })
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CommandRaiseTrigger"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="propertyToWatch">The property to watch.</param>
        /// <param name="propertiesToRaise">The properties to raise.</param>
        public CommandRaiseTrigger(IEntity source, Expression<Func<IEntity, object>> propertyToWatch, params Expression<Func<object>>[] propertiesToRaise)
            : this(source, new Expression<Func<IEntity, object>>[] { propertyToWatch }, propertiesToRaise)
        {

        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles the WatchedPropertyChanged event of the ret control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        protected override void ret_WatchedPropertyChanged(object sender, EventArgs e)
        {
            foreach (var propertyToRaise in this.propertiesToRaise)
            {
                var unaryValue = propertyToRaise.Body as UnaryExpression;
                dynamic t = unaryValue != null ? unaryValue.Operand : propertyToRaise.Body;
                this.Source.RaisePropertyChangedEvent(t.Member.Name);
                if (t.Type == typeof(DelegateCommand))
                {
                    PropertyInfo pr = t.Member as PropertyInfo;
                    DelegateCommand dc = pr.GetValue(this.Source, null) as DelegateCommand;
                    if (dc != null)
                    {
                        dc.RaiseCanExecuteChanged();
                    }
                }
            }
        }

        #endregion
    }


    /// <summary>
    /// Command raise trigger
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CommandRaiseTrigger<T> : RaiseTrigger<T>
        where T : IEntity
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="CommandRaiseTrigger{T}"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="propertiesToWatch">The properties to watch.</param>
        /// <param name="propertiesToRaise">The properties to raise.</param>
        public CommandRaiseTrigger(T source, Expression<Func<T, object>>[] propertiesToWatch, Expression<Func<object>>[] propertiesToRaise)
            : base(source, propertiesToWatch, propertiesToRaise)
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CommandRaiseTrigger{T}"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="propertyToRaise">The property to raise.</param>
        /// <param name="propertiesToWatch">The properties to watch.</param>
        public CommandRaiseTrigger(T source, Expression<Func<object>> propertyToRaise, params Expression<Func<T, object>>[] propertiesToWatch)
            : this(source, propertiesToWatch, new Expression<Func<object>>[] { propertyToRaise })
        {

        }


        /// <summary>
        /// Initializes a new instance of the <see cref="CommandRaiseTrigger{T}"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="propertyToWatch">The property to watch.</param>
        /// <param name="propertiesToRaise">The properties to raise.</param>
        public CommandRaiseTrigger(T source, Expression<Func<T, object>> propertyToWatch, params Expression<Func<object>>[] propertiesToRaise)
            : this(source, new Expression<Func<T, object>>[] { propertyToWatch }, propertiesToRaise)
        {

        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles the WatchedPropertyChanged event of the ret control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        protected override void ret_WatchedPropertyChanged(object sender, EventArgs e)
        {
            foreach (var propertyToRaise in this.propertiesToRaise)
            {
                var unaryValue = propertyToRaise.Body as UnaryExpression;
                dynamic t = unaryValue != null ? unaryValue.Operand : propertyToRaise.Body;
                this.Source.RaisePropertyChangedEvent(t.Member.Name);
                PropertyInfo pr = t.Member as PropertyInfo;
                if (pr != null)
                {
                    DelegateCommand dc = pr.GetValue(this.Source, null) as DelegateCommand;
                    if (dc != null)
                    {
                        dc.RaiseCanExecuteChanged();
                    }
                }
            }
        }

        #endregion
    }
}
